﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace PushDownAutomaton
{
    public class PDA
    {
        private HashSet<PDAState> states;
        private HashSet<char> inputAlphabet;
        private HashSet<String> stackAlphabet;
        private DeltaPDA delta;
        private PDAState initialState;
        private String initialStackSymbol;
        private HashSet<PDAState> finalStates;

        public PDA(HashSet<PDAState> states,
                   HashSet<char> inputAlphabet,
                   HashSet<String> stackAlphabet,
                   DeltaPDA delta,
                   PDAState initialState,
                   String initialStackSymbol,
                   HashSet<PDAState> finalStates)
        {
            this.states = states;
            this.inputAlphabet = inputAlphabet;
            this.stackAlphabet = stackAlphabet;
            this.delta = delta;
            this.initialState = initialState;
            this.initialStackSymbol = initialStackSymbol;
            this.finalStates = finalStates;
        }

        public Boolean ProcessWord(String word, PDAMode mode)
        {
            Stack<String> stack = new Stack<string>();

            // Initial symbol in stack
            stack.Push(initialStackSymbol);

            // Initial automaton's state
            PDAState currentState = initialState;

            return ProcessWord(currentState, word, stack, mode);
        }

        public Boolean ProcessWord(PDAState currentState, 
                                   String remainingWord, 
                                   Stack<String> stack,
                                   PDAMode mode)
        {
            // Base case depends on mode
            if (mode == PDAMode.ByFinalState)
            {
                if (remainingWord == "" && finalStates.Contains(currentState))
                    return true;
            }
            else if (mode == PDAMode.ByEmptyStack)
            {
                if (remainingWord == "" && stack.Count == 0)
                    return true;
            }

            char c = remainingWord[0];

            //remnianginwordddddd
            
            HashSet<DeltaPDAValue> values = delta.Evaluate(currentState, c, stack.Peek());

            foreach (DeltaPDAValue value in values)
            {
                // Accion sobre el stack
                if      (value.Action == DeltaPDAAction.Pop) stack.Pop();
                else if (value.Action == DeltaPDAAction.Push) stack.Push(value.PushSymbol);

                if (ProcessWord(value.NextState, remainingWord, stack, mode))
                    return true;
            }

            return false;
        }

        public Boolean AcceptsByFinalState(String word)
        {
            return ProcessWord(word, PDAMode.ByFinalState);
        }

        public Boolean AcceptsByEmptyStack(String word)
        {
            return ProcessWord(word, PDAMode.ByEmptyStack);
        }

    }
}
